package im.youni.horus.core.common;

import im.youni.horus.core.SecondTag;
import im.youni.horus.core.common.Constants;

import java.io.IOException;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class TextFormat {

    static int[] buckets = new int[14];

    static {
        buckets[0] = 1;
        buckets[1] = 5;
        buckets[2] = 10;
        buckets[3] = 25;
        buckets[4] = 50;
        buckets[5] = 75;
        buckets[6] = 100;
        buckets[7] = 250;
        buckets[8] = 500;
        buckets[9] = 750;
        buckets[10] = 1000;
        buckets[11] = 1500;
        buckets[12] = 3000;
        buckets[13] = 10000;
    }

    // 指标1
    private static String histogram_Help = "api_request_duration_seconds Histogram of latencies for api requests.";
    private static String histogram_Type = "api_request_duration_seconds histogram";
    private static String histogram_metricName = "api_request_duration_seconds";
    private static String histogram_metricCountName = "api_request_duration_count";
    private static String histogram_metricSumName = "api_request_duration_sum";

    // 指标2
    private static String counter_metricName = "api_request_error_total";
    private static String counter_Help = "api_request_error_total error counter api requests.";
    private static String counter_Type = "api_request_error_total counter";


    public static void write(Writer writer, ConcurrentHashMap<SecondTag, String> metrics) throws IOException {
        if (metrics.size() > 0) {
            // 指标1: 耗时bucket
            writeTitle(writer, Constants.Metrics.api_request_second);
            for (Map.Entry<SecondTag, String> entry : metrics.entrySet()) {
                writeBucket(writer, entry.getKey(), entry.getValue());
            }

            // 指标2: 错误信息
            writeTitle(writer, Constants.Metrics.api_request_error);
            writeCounter(writer, metrics);
        }
    }

    /**
     * 输出metrics的Help\Type信息头
     * @param writer
     * @param metricType
     * @throws IOException
     */
    private static void writeTitle(Writer writer, int metricType) throws IOException {
        writer.write("# HELP ");
        writer.write(metricType == Constants.Metrics.api_request_second ? histogram_Help : counter_Help);
        writer.write(10);
        writer.write("# TYPE ");
        writer.write(metricType == Constants.Metrics.api_request_second ? histogram_Type : counter_Type);
        writer.write(10);
    }

    /**
     * 输出Bucket信息
     * @param writer
     * @param tags
     * @throws IOException
     */
    private static void writeCounter(Writer writer, ConcurrentHashMap<SecondTag, String> tags) throws IOException {
        for (SecondTag item : tags.keySet()) {
            writer.write(errorToMetric(item, counter_metricName));
        }
    }

    /**
     * 输出Bucket信息
     * @param writer
     * @param tag
     * @param time
     * @throws IOException
     */
    private static void writeBucket(Writer writer, SecondTag tag, String time) throws IOException {
        String[] timeArr = time.split(",");
        Map<Integer, Integer> timeMap = new HashMap<>();

        long toalcount = 0;
        long totalSum = 0;

        for (int i = 0; i < timeArr.length; i++) {
            if (i % 2 == 0) {
                int seconds = Integer.parseInt(timeArr[i].trim());
                int apiCount = Integer.parseInt(timeArr[i + 1].trim());
                timeMap.put(seconds, apiCount);
                toalcount += apiCount;
                totalSum += seconds * apiCount;
            }
        }

        // 输出不同的Bucket桶信息
        for (Integer item : buckets) {
            int bucketCount = 0;
            for (Map.Entry<Integer, Integer> entry : timeMap.entrySet()) {
                if (entry.getKey() <= item) {
                    bucketCount += entry.getValue();
                }
            }
            writer.write(tagToMetric(tag, histogram_metricName, item, bucketCount));
        }

        // 输出bucket尾部信息
        writer.write(tagToCountMetric(tag, histogram_metricCountName, toalcount));
        writer.write(tagToSumMetric(tag, histogram_metricSumName, totalSum));
    }

    /**
     * tag信息转为Metrics指标
     * @param tag
     * @param metricName
     * @param le
     * @param sample
     * @return
     */
    private static String tagToMetric(SecondTag tag, String metricName, long le, long sample) {
        return String.format(
                "%s{app=\"%s\",service=\"%s\",method=\"%s\",ip=\"%s\", le=\"%s\"} %d %d\n",
                metricName,
                tag.app,
                tag.service,
                tag.method,
                tag.local,
                (le == 10000 ? "+inf" : String.valueOf(le)),
                sample,
                tag.time * 1000);
    }

    /**
     * tag信息转为Metrics指标
     * @param tag
     * @param metricName
     * @return
     */
    private static String errorToMetric(SecondTag tag, String metricName) {
        return String.format(
                "%s{app=\"%s\",service=\"%s\",method=\"%s\",ip=\"%s\", errorType=\"%d\", errorCode=\"%d\"} %d %d\n",
                metricName,
                tag.app,
                tag.service,
                tag.method,
                tag.local,
                tag.errorType,
                tag.errorCode,
                tag.errorCount,
                tag.time * 1000);
    }

    /**
     * tag转为count
     * @param tag
     * @param metricName
     * @param sample
     * @return
     */
    private static String tagToCountMetric(SecondTag tag, String metricName, long sample) {
        return String.format(
                "%s{app=\"%s\",service=\"%s\",method=\"%s\",ip=\"%s\"} %d %d\n",
                metricName,
                tag.app,
                tag.service,
                tag.method,
                tag.local,
                sample,
                (long) tag.time * 1000);
    }

    private static String tagToSumMetric(SecondTag tag, String metricName, long sample) {
        return String.format(
                "%s{app=\"%s\",service=\"%s\",method=\"%s\",ip=\"%s\"} %d %d\n",
                metricName, tag.app,
                tag.service,
                tag.method,
                tag.local,
                sample,
                (long) tag.time * 1000);
    }
}
